#include <string.h>
#include <stdint.h>
#include "malloc.h"

const uint32_t block_struct_size = sizeof(mem_block_t);

//#define BLOCK_ALIGN(size,block) (((size) + (block) - 1) & ~((block) - 1))

/**
 * @brief memory pool init
 * 
 * @param pool  
 * @param head 首地址，需要4字节对齐，最好用int型指针
 * @param size 大小，为1字节的大小
 * @param unit block size 大小
 * @return int8_t 0 normal , other error 
 */
int8_t mem_init(mem_pool_t* pool,uint32_t *head, uint32_t size,uint32_t unit)
{
    if((pool == NULL) || (head == NULL) || (size == 0))
    {
        return -1;
    }

#if 1
    mem_block_t *t = (mem_block_t *)head;
    t->next = 0;
    t->prev = 0;
    t->used = 0;
    t->size = size - block_struct_size;

    pool->head = head;
    pool->tail = (uint32_t *)((uint32_t)head + size);
    pool->list = t;
    pool->available_size = t->size;
    pool->block_size = unit;
#else
    mem_block_t *t;
    // tail
    t = (mem_block_t *)((uint32_t)head + size - block_struct_size);
    t->used = 1;
    t->size = 0;
    t->next = 0;
    pool->tail = t;
    // head
    t = (mem_block_t *)head; 
    t->used = 1;
    t->size = 0;
    t->prev = 0;
    pool->head = t;
    // free
    t = (mem_block_t *)((uint32_t)head + block_struct_size);
    t->used = 0;
    t->size = size - block_struct_size * 3;
    t->prev = pool->head;
    t->next = pool->tail;
    pool->free = t;

    pool->available_size = pool->free->size;
    pool->block_size = unit;
    #endif

    return 0;
}

/**
 * Allocate a block of memory with a minimum of 'size' bytes.
 *
 * @param size is the minimum size of the requested block in bytes.
 *
 * @return pointer to allocated memory or NULL if no free memory was found.
 */
void *mem_malloc(mem_pool_t* pool,uint32_t size)
{
    if (size == 0)
        return MEM_NULL;

    size += block_struct_size;
    size = MEM_ALIGN_UP(size,pool->block_size);
    if (size > pool->available_size)
    {
        return MEM_NULL;
    }

    for (mem_block_t *t = pool->list;;t=t->next)
    {
        if(t == MEM_NULL)
        {
            return MEM_NULL;
        }
        if(t->used == 1 || t->size < size)
        {
            continue;
        }

        mem_block_t* free = 0;
        if (t->size >= (size + block_struct_size + pool->block_size))
        {
            free = (mem_block_t*)((uint32_t)t + size);
            free->size = t->size - size;//(size - block_struct_size) - block_struct_size;
            free->used = 0;
            free->prev = t;
            free->next = t->next;

            t->used = 1;
            t->size = size - block_struct_size;
            t->next = free;
        }
        else //if ((t->size >= size)&&(t->size < (size + block_struct_size)))
        {
            //while ((t->next != 0) )
            //{
            //    free = t->next;
            //}
            t->used = 1;
            t->size = t->size - block_struct_size;
            t->next = t->next;
        }

        return (void *)((uint32_t)t + block_struct_size);
    }

    return MEM_NULL;
}
#if 0
/**
 * This function will change the previously allocated memory block.
 *
 * @param rmem pointer to memory allocated by rt_malloc
 * @param newsize the required new size
 *
 * @return the changed memory block address
 */
void *rt_realloc(void *rmem, rt_size_t newsize)
{
    rt_size_t size;
    rt_size_t ptr, ptr2;
    struct heap_mem *mem, *mem2;
    void *nmem;


    /* alignment size */
    newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);
    if (newsize > mem_size_aligned)
    {
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("realloc: out of memory\n"));

        return RT_NULL;
    }
    else if (newsize == 0)
    {
        rt_free(rmem);
        return RT_NULL;
    }

    /* allocate a new memory block */
    if (rmem == RT_NULL)
        return rt_malloc(newsize);


    if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr ||
        (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end)
    {
        /* illegal memory */
        return rmem;
    }

    mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);

    ptr = (rt_uint8_t *)mem - heap_ptr;
    size = mem->next - ptr - SIZEOF_STRUCT_MEM;
    if (size == newsize)
    {
        /* the size is the same as */
        return rmem;
    }

    if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size)
    {
        /* split memory block */
#ifdef RT_MEM_STATS
        used_mem -= (size - newsize);
#endif

        ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
        mem2 = (struct heap_mem *)&heap_ptr[ptr2];
        mem2->magic = HEAP_MAGIC;
        mem2->used = 0;
        mem2->next = mem->next;
        mem2->prev = ptr;
#ifdef RT_USING_MEMTRACE
        rt_mem_setname(mem2, "    ");
#endif
        mem->next = ptr2;
        if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM)
        {
            ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2;
        }
        
        if (mem2 < lfree)
        {
            /* the splited struct is now the lowest */
            lfree = mem2;
        }

        plug_holes(mem2);
        return rmem;
    }
   
    /* expand memory */
    nmem = rt_malloc(newsize);
    if (nmem != RT_NULL) /* check memory */
    {
        memcpy(nmem, rmem, size < newsize ? size : newsize);
        rt_free(rmem);
    }

    return nmem;
}
#endif
/**
 * This function will release the previously allocated memory block by
 * rt_malloc. The released memory block is taken back to system heap.
 *
 * @param rmem the address of memory which will be released
 */
int8_t mem_free(mem_pool_t* pool,void *mem)
{
    if (pool == MEM_NULL)
        return 1;

    if (mem == MEM_NULL)
        return 2;

    for(mem_block_t *t = pool->list;;t=t->next)
    {
        if (t == MEM_NULL)
        {
            return 3;
        }    
        if(t->used != 1)
        {
            continue;
        }
        if(((uint32_t)t + block_struct_size) != (uint32_t)mem)
        {
            continue;
        }

        t->used = 0;
        if((t->next != 0) && (t->next->used == 0))
        {
            mem_block_t* tmp = t->next;
            t->size += block_struct_size + tmp->size;
            t->next = tmp->next;
            if(tmp->next != 0)
            tmp->next->prev = t;
        }
        if((t->prev != 0) && (t->prev->used == 0))
        {
            mem_block_t* tmp = t->prev;
            tmp->size += block_struct_size + t->size;
            tmp->next = t->next;
            if (t->next != 0)
            t->next->prev = tmp;
        }
        
        break;
    }

    return 0;
}


